/*******************************************************************************
Graphics.cpp

	Author:		LiuDingXi
	e-mail:		liudx@gmail.com
	Date time:	2009-09-15

	Copyright (C) 2009
	All rights reserved.
	
*******************************************************************************/
#include "StdAfx.h"
#include "Graphics.h"
#include <math.h>

#if (_OS_ == _OS_WINNT_)
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#endif//(_OS_ == _OS_WINNT_)

Graphics::Graphics(HGC hGC) : m_hGC(hGC)
{
	m_nMaxClipWidth = gcWidth();
	m_nMaxClipHeight = gcHeight();
}
Graphics::~Graphics()
{
}

int Graphics::gcWidth()const
{
	return m_hGC.nWidth;
}

int Graphics::gcHeight()const
{
	return m_hGC.nHeight;
}

void Graphics::SetColor(const GColor& color)
{
	glColor4f(color.red, color.green, color.blue, color.alpha);
}

void Graphics::DrawPoint(const GPoint& point)
{
	GLfloat vertices[] = {
		point.x,	point.y,	1,
	};
	
	glDisable( GL_TEXTURE_2D );
	
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glDrawArrays(GL_POINTS, 0, 1);
	
	glEnable(GL_TEXTURE_2D);
}

void Graphics::DrawPoints(const GPoint* pPoints, int nCount)
{
	GLfloat* vertices = new GLfloat[nCount * 3] ;
	
	for(int i = 0; i < nCount; i++)
	{
		vertices[i * 3] = pPoints[i].x;
		vertices[i * 3 + 1] = pPoints[i].y;
		vertices[i * 3 + 2] = 1;
	}

	
	glDisable( GL_TEXTURE_2D );
	
	glVertexPointer(3, GL_FLOAT, 0, pPoints);
	
	glDrawArrays(GL_POINTS, 0, nCount);
	
	glEnable(GL_TEXTURE_2D);

	delete[] vertices;
}


void Graphics::DrawLine(const GPoint& pt1, const GPoint& pt2)
{
	GLfloat vertices[] = {
		pt1.x,		pt1.y,		1,
		pt2.x,		pt2.y,		1,
	};
	
	glDisable( GL_TEXTURE_2D );
	
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	
	glDrawArrays(GL_LINES, 0, 2);
	
	glEnable(GL_TEXTURE_2D);
}

void Graphics::DrawRect(const GRect& rect)
{
	GLfloat vertices[] = {
		rect.x,				rect.y,				1,
		rect.x + rect.cx,	rect.y,				1,
		rect.x + rect.cx,	rect.y + rect.cy,	1,
		rect.x,				rect.y + rect.cy,	1
	};
	
	glDisable(GL_TEXTURE_2D);
	
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glDrawArrays(GL_LINE_LOOP, 0, 4);
	
	glEnable(GL_TEXTURE_2D);
}


void Graphics::FillRect(const GRect& rect)
{
	GLfloat vertices[] = {
		rect.x,				rect.y,				1,
		rect.x + rect.cx,	rect.y,				1,
		rect.x,				rect.y + rect.cy,	1,
		rect.x + rect.cx,	rect.y + rect.cy,	1
	};
	
	glDisable(GL_TEXTURE_2D);
	
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	
	glEnable(GL_TEXTURE_2D);
}

void GArc::Normalize()
{
	if(startAngle < 0)
	{
		startAngle += 0x7fffff80;
		if(startAngle < 0)
			startAngle += 360;
		else
			startAngle %= 360;
	} 
	else
	{
		startAngle %= 360;
	}

	if(sweepAngle >= 360 || sweepAngle <= -360)
	{
		startAngle = 0;
		sweepAngle = 360;
	}
	else if(sweepAngle < 0)
    {
        startAngle += sweepAngle;
        sweepAngle = -sweepAngle;
        if(startAngle < 0)
            startAngle += 360;
    }
}

void Graphics::drawArc(const GArc& arc, bool bFill)
{
	int k3 = arc.startAngle + arc.sweepAngle;
	int k2 = (arc.cx + 1) >> 1;
	int l2 = (arc.cy + 1) >> 1;
	int l4 = 0 - arc.y;
	int i5 = (arc.y + arc.cy) - 0 - m_nMaxClipHeight;
	int i3;
	int j3;
	if(l4 > l2)
	{
		i3 = i5 <= 0 ? 0 : i5;
		j3 = arc.cy - l4;
	}
	else if(i5 > l2)
    {
        i3 = l4 <= 0 ? 0 : l4;
        j3 = arc.cy - i5;
    }
	else if(l4 >= 0 && i5 >= 0)
	{
		i3 = l4 >= i5 ? i5 : l4;
		j3 = l2;
	}
	else 
	{
		i3 = 0;
		j3 = l2;
	}
	if(j3 <= i3)
		return;

	int length = j3-i3;
	int* ai = new int[length];
	setArcProperties(ai, i3, j3, k2, l2);

	int l3 = arc.startAngle / 90;
	int i4 = l3 * 90;
	int j4 = arc.startAngle - i4;
	for(; i4 < k3; i4 += 90)
	{
		int k4 = k3 - i4;
		if(k4 > 90)
			k4 = 90;
		if((l3 & 1) != 0)
		{
			int j5 = 90 - j4;
			j4 = 90 - k4;
			k4 = j5;
		}
		int k5;
		int l5;
		if(j4 != 0)
		{
			double d1 = (double)j4 * 0.017453292519943295;
			k5 = k2 - (int)((double)k2 * cos(d1));
			l5 = l2 - (int)((double)l2 * sin(d1));
		}
		else
		{
			k5 = 0;
			l5 = l2;
		}
		int i6;
		int j6;
		if(k4 != 90)
		{
			double d2 = (double)k4 * 0.017453292519943295;
			i6 = k2 - (int)((double)k2 * cos(d2));
			j6 = l2 - (int)((double)l2 * sin(d2));
		}
		else
		{
			i6 = k2;
			j6 = 0;
		}
		setArcIndexedPixel(arc.x, arc.y, arc.cx, arc.cy,
			l3, k5, l5, i6, j6, i3, ai, length, bFill);
			
		j4 = 0;
		l3 = l3 + 1 & 3;
	}
	delete[] ai;
}


void Graphics::setArcProperties(int* ai, int i1, int j1, int k1, int l1)
{
	long l2 = (long)k1 * (long)k1;
	long l3 = l2 << 1;
	long l4 = l3 << 1;
	long l5 = (long)l1 * (long)l1;
	long l6 = l5 << 1;
	long l7 = l6 << 1;
	long l8 = (long)k1 * l6;
	long l9 = l8 << 1;
	long l10 = 0L;
	long l11 = ((l5 >> 1) + l3) - l8;
	long l12 = ((l2 >> 1) + l6) - l9;
	int i2 = 0;
	int j2;
	for(j2 = l1; l12 < 0L && j2 >= i1;)
	{
		if(j2 < j1)
			ai[j2 - i1] = i2;
		j2--;
		l10 += l4;
		if(l11 < 0L)
		{
			l11 += l10 + l3;
			l12 += l10;
		} else
		{
			i2++;
			l9 -= l7;
			l11 += (l10 + l3) - l9;
			l12 += (l10 + l6) - l9;
		}
	}
	
	if(j2 >= i1 && j2 < j1)
		ai[j2 - i1] = i2;
	while(j2 > i1) 
	{
		i2++;
		l9 -= l7;
		if(l12 < 0L)
		{
			if(--j2 < j1)
				ai[j2 - i1] = i2;
			l10 += l4;
			l12 += (l10 + l6) - l9;
		} else
		{
			l12 += l6 - l9;
		}
	}
}


void Graphics::setArcIndexedPixel(int i1, int j1, int k1, int l1,
								  int i2, int j2, int k2, int l2,
								  int i3, int j3,
								  int * ai, int length, bool bFill)
{
	int l4 = i1 + k1;
	int i5 = j1 + l1;
	int j5 = m_nMaxClipWidth;
	int k5 = m_nMaxClipHeight;
	bool flag = (i2 & 2) == 0;
	bool flag1 = (i2 + 1 & 2) != 0;
	int k3 = (k1 + 1) >> 1;
	int l3 = j3;
	int i4 = 0;
	int j4 = ai[0];
	
	GLshort* vertices = new GLshort[length * k3 * 3 + (bFill ? 3 : 0)];
	int vlen = 0;
	if(bFill)
	{
		vertices[0 * 3] = flag1 ? i1 +  k1: l4 - k1;
		vertices[0 * 3 + 1] = flag ? j1 + l1 : i5 - l1;
		vertices[0 * 3 + 2] = 1;
		vlen++;
	}
	while(k3 >= 0 && i4 <= length) 
	{
		if(k3 >= j2 && k3 <= l2 && l3 <= k2 && l3 >= i3)
		{
			int l5 = flag1 ? i1 + k3 : l4 - k3;
			int i6 = flag ? j1 + l3 : i5 - l3;
			if(l5 >= 0 && l5 < j5 && i6 >= 0 && i6 < k5)
			{
				
				vertices[vlen * 3] = l5;
				vertices[vlen * 3 + 1] = i6;
				vertices[vlen * 3 + 2] = 1;
				vlen++;
			}
				
		}
		if(k3 > j4 + 1)
		{
			k3--;
		} else
		{
			k3 = j4;
			l3++;
			j4 = ++i4 >= length ? 0 : ai[i4];
		}
	}
	
	
	int hw = (k1 + 1) >> 1;
	int hh = (l1 + 1) >> 1;
	
	glDisable( GL_TEXTURE_2D );
	glVertexPointer(3, GL_SHORT, 0, vertices);
	glDrawArrays(bFill ? GL_TRIANGLE_FAN : GL_LINE_STRIP, 0, vlen);
	
	glEnable(GL_TEXTURE_2D);
	
	delete[] vertices;
}

void Graphics::DrawArc(const GArc& arc)
{
	drawArc(arc, false);
}

void Graphics::FillArc(const GArc& arc)
{
	drawArc(arc, true);
}

void Graphics::DrawRbox(const GRbox& rbox)
{
	int k2 = rbox.x + rbox.cx;
	int l2 = rbox.x + rbox.arc_x;
	int i3 = k2 - rbox.arc_x;
	int j3 = rbox.arc_x << 1;
	int k3 = k2 - j3;
	int l3 = rbox.y + rbox.cy;
	int i4 = rbox.y + rbox.arc_y;
	int j4 = l3 - rbox.arc_y;
	int k4 = rbox.arc_y << 1;
	int l4 = l3 - k4;
	
	GPoint p1;
	GPoint p2;

	p1.x = l2; p1.y = rbox.y;
	p2.x = i3; p2.y = rbox.y;
	
	DrawLine(p1, p2);

	p1.x = l2; p1.y = l3;
	p2.x = i3; p2.y = l3;

	DrawLine(p1, p2);

	p1.x = rbox.x; p1.y = i4;
	p2.x = rbox.x; p2.y = j4;

	DrawLine(p1, p2);

	p1.x = k2; p1.y = i4;
	p2.x = k2; p2.y = j4;

	DrawLine(p1, p2);

	GArc arc;
	arc.x = k3;
	arc.y = rbox.y;
	arc.cx = j3;
	arc.cy = k4;
	arc.startAngle = 0;
	arc.sweepAngle = 90;
	DrawArc(arc);

	arc.x = rbox.x;
	arc.y = rbox.y;
	arc.cx = j3;
	arc.cy = k4;
	arc.startAngle = 90;
	arc.sweepAngle = 90;
	DrawArc(arc);

	arc.x = rbox.x;
	arc.y = l4;
	arc.cx = j3;
	arc.cy = k4;
	arc.startAngle = 180;
	arc.sweepAngle = 90;
	DrawArc(arc);

	arc.x = k3;
	arc.y = l4;
	arc.cx = j3;
	arc.cy = k4;
	arc.startAngle = 270;
	arc.sweepAngle = 90;
	DrawArc(arc);
}

void Graphics::FillRbox(const GRbox& rbox)
{
	int k2 = rbox.arc_x << 1;
	int l2 = rbox.x + rbox.arc_x;
	int i3 = (rbox.x + rbox.cx) - k2;
	int j3 = i3 + rbox.arc_x;
	int k3 = rbox.arc_y << 1;
	int l3 = rbox.y + rbox.arc_y;
	int i4 = (rbox.y + rbox.cy) - k3;

	GArc arc;
	arc.x = i3;
	arc.y = rbox.y;
	arc.cx = k2;
	arc.cy = k3;
	arc.startAngle = 0;
	arc.sweepAngle = 90;
	FillArc(arc);

	arc.x = rbox.x;
	arc.y = rbox.y;
	arc.cx = k2;
	arc.cy = k3;
	arc.startAngle = 90;
	arc.sweepAngle = 90;
	FillArc(arc);

	arc.x = rbox.x;
	arc.y = i4;
	arc.cx = k2;
	arc.cy = k3;
	arc.startAngle = 180;
	arc.sweepAngle = 90;
	FillArc(arc);

	arc.x = i3;
	arc.y = i4;
	arc.cx = k2;
	arc.cy = k3;
	arc.startAngle = 270;
	arc.sweepAngle = 90;
	FillArc(arc);

	GPoint pt1, pt2, pt3;
	pt1.x = l2;
	pt1.y = rbox.y;

	pt2.x = pt1.x + rbox.cx - k2;
	pt2.y = pt1.y;

	pt3.x = rbox.x + k2;
	pt3.y = rbox.y + k3;

	FillTriangle(pt1, pt2, pt3);

	pt1.x = i3;
	pt1.y = rbox.y + k3;
	
	FillTriangle(pt1, pt2, pt3);

	pt2.x = rbox.x + rbox.cx;
	pt2.y = l3;

	pt3.x = pt2.x;
	pt3.y = i4 + rbox.arc_y;

	FillTriangle(pt1, pt2, pt3);

	pt2.x = pt1.x;
	pt2.y = i4;

	FillTriangle(pt1, pt2, pt3);

	pt1.x = i3 + rbox.arc_x;
	pt1.y = rbox.y + rbox.cy;

	pt3.x = l2;
	pt3.y = pt1.y;

	FillTriangle(pt1, pt2, pt3);

	pt1.x = rbox.x + k2;
	pt1.y = i4;

	FillTriangle(pt1, pt2, pt3);

	pt2.x = rbox.x;
	pt2.y = l3;

	pt3.x = pt2.x;
	pt3.y = i4 + rbox.arc_y;

	FillTriangle(pt1, pt2, pt3);

	pt3.x = rbox.x + k2;
	pt3.y = rbox.y + k3;

	FillTriangle(pt1, pt2, pt3);

	GRect rect;
	rect.x = pt3.x;
	rect.y = pt3.y;
	rect.cx = rbox.cx - k2 - k2;
	rect.cy = rbox.cy - k3 - k3;

	FillRect(rect);
}

void Graphics::DrawTriangle(const GPoint& pt1, const GPoint& pt2, const GPoint& pt3)
{
	DrawLine(pt1, pt2);
	DrawLine(pt2, pt3);
	DrawLine(pt3, pt1);
}

void Graphics::FillTriangle(const GPoint& pt1, const GPoint& pt2, const GPoint& pt3)
{
	GLfloat vertices[] = {
		pt1.x,				pt1.y,				1,
		pt2.x,				pt2.y,				1,
		pt3.x,				pt3.y,				1
	};
	
	glDisable(GL_TEXTURE_2D);
	
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glDrawArrays(GL_TRIANGLES, 0, 3);
	
	glEnable(GL_TEXTURE_2D);
}